﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Net;
using System.Reflection;
using System.Diagnostics;

namespace CorpStoreServer
{
    public class Main
    {
        const string STORE_PROTOCOL = "corpstore";
        public const string STORE_FAMILY_NAME = "CorpStore_zykmbad61hv5m";
        private string APPSETTINGS_FN = string.Format(@"{0}\AppSettings.json", GenLib.AppExePath());

        Log log = new Log();
        AppSettings appSettings = new AppSettings();

        public void Run(string[] args)
        {
            CorpStoreServer.Enums.eTask taskToRun;
            string strErr;
            NameValueCollection parms;
            bool consoleIsDisplayed = false;

            if (processCommandLine(args, out taskToRun, out parms))
            {
                // parsed command line ok - now show console window if necessary
                if (taskToRun == Enums.eTask.updateMasterCatalog && !GenLib.GetParm(parms, "mode").Equals("quiet"))
                {
                    GenLib.ShowConsoleWindow();
                    consoleIsDisplayed = true;
                }

                if (log.Init(string.Format(@"{0}\CorpStoreServer\CorpStoreServer.log", Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create)), true, out strErr))
                {
                    // initialized log file ok - init our app settings
                    // first attempt to read app settings
                    if (appSettings.Init(APPSETTINGS_FN, log))
                    {
                        // now triggger parts
                        logInfo("After log init.  Current Version = {0}, Current user context = {1}", GenLib.AppExeVer(), System.Security.Principal.WindowsIdentity.GetCurrent().Name);
                        switch (taskToRun)
                        {
                            case Enums.eTask.startWin32App:
                                {
                                    StartWin32App obj = new StartWin32App();
                                    obj.Run(log, parms);
                                    break;
                                }

                            case Enums.eTask.startStoreApp:
                                {
                                    StartStoreApp obj = new StartStoreApp();
                                    obj.Run(log, parms);
                                    break;
                                }

                            case Enums.eTask.startEnterpriseApp:
                                {
                                    StartEnterpriseApp obj = new StartEnterpriseApp();
                                    obj.Run(log, parms);
                                    break;
                                }

                            case Enums.eTask.updateMasterCatalog:
                                {
                                    UpdateMasterCatalog obj = new UpdateMasterCatalog();
                                    obj.Run(log, parms, appSettings);
                                    break;
                                }

                            case Enums.eTask.checkCommonCatalog:
                                {
                                    CheckCommonCatalog obj = new CheckCommonCatalog();
                                    obj.Run(log, parms, appSettings);
                                    break;
                                }

                            case Enums.eTask.setCommonCatalog:
                                {
                                    // update our app settings if any parameters passed
                                    logInfo("SetCommonCatalog action has started");
                                    string commonCatalogFn = GenLib.GetParm(parms, "commonCatalogFn");
                                    if (!string.IsNullOrEmpty(commonCatalogFn))
                                    {
                                        // commoncatalog filename was passed in - save it in our settings
                                        logInfo("commonCatalogFn parameter was passed to init - updating our appSettings with new fn '{0}'", commonCatalogFn);
                                        commonCatalogFn = GenLib.ExpandEnvironmentVars(commonCatalogFn, out strErr);

                                        appSettings.CommonCatalogFn = commonCatalogFn;
                                        logInfo("CommonCatalogFn after expansion is '{0}'", commonCatalogFn);

                                        appSettings.LastCatalogChecksum = GenLib.GetFileHash(commonCatalogFn, out strErr);
                                        logInfo("current file hash for commonCatalog = '{0}'", appSettings.LastCatalogChecksum);
                                        if (appSettings.Save())
                                            logInfo("Updated {0} with new commonCatalogFn '{1}' Ok", APPSETTINGS_FN, commonCatalogFn);
                                        else
                                            logErr("Error updating {0} with passed in commonCatalogFn: {1}", APPSETTINGS_FN, appSettings.lastErrStr());
                                    }
                                    else
                                        logInfo("commonCatalogFn is a required parameter but not passed in - cannot set");

                                    break;
                                }

                            default:
                                log.LogErr("Unsupported task provided");
                                break;
                        } // switch taskToRun
                    }
                    else
                        logErr("Error initializing app settings - cannot continue: {0}", appSettings.lastErrStr());

                    log.Shutdown();
                }
                else
                    Debug.WriteLine(string.Format("Error initializing logger: {0}", strErr));
            }
            else
                if (!(args.Length == 1 && args[0] == "?"))
                    Debug.WriteLine("Error parsing command line arguments.  Ignoring request");

            //// wait for a key before closing console window
            //if (consoleIsDisplayed)
            //    Console.ReadKey();
        }




        private bool processCommandLine(string[] args, out Enums.eTask taskToRun, out NameValueCollection parms)
        {
            bool blnOK = false;
            string taskName;
            UriBuilder uri;

            taskToRun = Enums.eTask.unknown;
            parms = new NameValueCollection();

            try
            {
                logInfo("Num args passed = {0}", args.Length);
                if (args.Length >= 1)
                {
                    // check if protocol included in remaining portion of command line - if not, prepend it
                    logInfo("Arg 0 = '{0}'", args[0]);
                    string protocolStr = STORE_PROTOCOL + @"://";
                    if (args[0].StartsWith(protocolStr, StringComparison.OrdinalIgnoreCase))
                    {
                        // we were called via protocol - create URI from it and parse parms
                        uri = new UriBuilder(args[0]);

                        // parse query string parms 
                        parms = uri.ParseQuery();
                        logInfo("Number of parms found on query line = {0}", parms.Count);
                        taskName = uri.Host;

                        blnOK = nameToTask(taskName, out taskToRun);
                    }
                    else
                    {
                        // we were called via the command line, not protocol - parse out parms with space delimiter
                        parms = new NameValueCollection();

                        taskName = args[0];
                        if (taskName == "?")
                            showSyntax();
                        else
                        {

                            for (int i = 1; i < args.Length; i++)
                            {
                                string[] subParts = args[i].Split('=');
                                if (subParts.Length == 2)
                                {
                                    logInfo("Parsed command line parm {0} with value {1}", subParts[0], subParts[1]);
                                    parms.Add(subParts[0], subParts[1]);
                                }
                                else
                                    logErr("Ignoring part {0} since it does not contain an '='; part = '{1}'", i, args[i]);
                            }
                            blnOK = nameToTask(taskName, out taskToRun);
                        }
                    }
                } // we have at least 1 argument

                // handle case when no query parms passed at all
                if (taskToRun == Enums.eTask.unknown && parms.Count == 0 && (args.Length == 0 || args[0] != "?"))
                {
                    logInfo("No parms found - assuming we want to rebuild master catalog");
                    taskToRun = Enums.eTask.updateMasterCatalog;
                    blnOK = true;
                }
            }
            catch (Exception ex)
            {
                logErr("Unhandled exception in processCommandLine: {0}", ex.Message);
            }

            return blnOK;
        }


        private void showSyntax()
        {
            logInfo("\n\n\n" +
                     "CorpStoreServer Syntax:\n" +
                     "-----------------------\n" +
                     "CorpStoreServer [startWin32App exeName=<full path to win32 exe> [args=\"<optional arguments>\"]] | \n" +
                     "                [startEnterpriseApp familyName=<Package Family Name>|displayName=<package Display Name> appxFn=<full path to *.appx file>] | \n" +
                     "                [startStoreApp familyName=<Package Family Name>|displayName=<package Display Name> storeID=<store GUID from Windows Store share charm>] \n" +
                     "                [updateMasterCatalog commonCatalogFn=<full path to common catalog json file>] \n" +
                     "                [setCommonCatalog commonCatalogFn=<full path to common catalog json file>] \n" +
                     "\n" +
                     "Examples:\n" +
                     "---------\n" +
                     "CorpStoreServer startWin32App exeName=\"c:\\windows\\system32\\notepad.exe\" \n" +
                     "CorpStoreServer startEnterpriseApp familyName= " + STORE_FAMILY_NAME + " appxFn=\"c:\\corpStore.appx\" \n" +
                     "CorpStoreServer startStoreApp displayName=\"Angry Birds Space\" storeID=\"8ece2571-91e0-4f2f-b7e5-b0b7944ced2d\" \n",
                     "CorpStoreServer updateMasterCatalog commonCatalogFn=commonCatalog.json");
 
        }
        private bool nameToTask(string taskName, out Enums.eTask task)
        {
            bool blnOK = true;

            task = Enums.eTask.unknown;
            switch (taskName.ToUpper())
            {
                case "STARTWIN32APP" : task = Enums.eTask.startWin32App; break;
                case "STARTSTOREAPP" : task = Enums.eTask.startStoreApp; break;
                case "STARTENTERPRISEAPP" : task = Enums.eTask.startEnterpriseApp; break;
                case "UPDATEMASTERCATALOG" : task = Enums.eTask.updateMasterCatalog; break;
                case "SETCOMMONCATALOG": task = Enums.eTask.setCommonCatalog; break;
                case "CHECKCOMMONCATALOG": task = Enums.eTask.checkCommonCatalog; break;
                default: blnOK = false; break;
            }

            return blnOK;
        }


        private void logInfo(string msg, params object[] parms)
        {
            if (log != null)
                log.LogInfo(msg, parms);
        }


        private void logErr(string msg, params object[] parms)
        {
            if (log != null)
                log.LogErr(msg, parms);
        }

    }
}
